#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/stat.h>
#include <errno.h>
#define INPUT_SIZE 100
#define ARRAY_SIZE 30

// input prototypes
int cdcmd(char *);
int echocmd(char *);
int handle_cmd1(char*, char**, int);
int handle_cmd2(char*, char**, char**);
int handle_redir(char*, char**, char*, int);

// redirection prototypes
int redirect_in(char**, char*);
int redirect_out(char**, char*);
int mypipe(char**, char**);

// background and pathing prototypes
int addpath (char**);
int myexec(char**, int);
void death();

int main()
     /*********************************************************
       This function is the main driver of our shell program.
       It is basically just a loop that directs our input to its 
       proper place.
     **********************************************************/ 
{
  char input[INPUT_SIZE];       /* buffer for use for input only */
  char tmp[INPUT_SIZE];         /* buffer for general copy usage */
  char file[INPUT_SIZE];        /* buffer used for handle_redir */
  int bg, dir;                  /* flags used for backgrounding and redirecting */
  char * cmd1[ARRAY_SIZE];	/* used to handle most commands */
  char * cmd2[ARRAY_SIZE];	/* used in handle_cmd2 for the 2nd command */
  char * in;			/* used to eliminate leading whitespace */ 

  signal(SIGCHLD, &death);	/* signal handler */

/* upon entry of the shell, update the shell and pwd variable */
  setenv("SHELL", "myshell", 1);
  setenv("PWD", getcwd(tmp, INPUT_SIZE), 1);

/* menu driver */
  while(1) {
    /* clears the buffers so nothing is left to tack on to the end of the strings */	
    bzero(input, INPUT_SIZE);
    bzero(tmp, INPUT_SIZE);
    bzero(file, INPUT_SIZE);
    fflush(stdin);
    
    printf("myshell~> ");
    if(fgets(input, INPUT_SIZE, stdin) == 0)
      fprintf(stderr, "Input error\n");

    input[strlen(input) - 1] = '\0';     /* strips newline off of the input buffer */

    /* eats up leading tabs and whitespace */
    in = input;	
    while((*in == ' ') || (*in == '\t')) { in++; }
    strcpy(tmp, in);
    bzero(input, INPUT_SIZE);
    strcpy(input, tmp);
    bzero(tmp, INPUT_SIZE);
    in = NULL;

    /* EXIT */
    /* assumes no one is going to make a call with something called "exit" in it */
    if(strstr("exit", input) != 0) {
      break;
    }
    /* ECHO */
    /* assumes that "echo" will be the first 4 characters */
    else if(strncmp(input, "echo", 4) == 0) {
      if(echocmd(input) != 0)
	fprintf(stderr, "Failed to echo\n");
    }    
    /* CHANGE DIRECTORY */
    /* assumes that "cd" will be the first 4 characters */
    else if(strncmp(input, "cd", 2) == 0) {
      if (cdcmd(input) != 0)
	fprintf(stderr, "Failed to change directory\n");
    }
    /* PWD */
    /* assumes that "pwd" will be the first 4 characters */
    else if(strncmp(input,"pwd", 3) == 0) {
      getcwd(tmp, INPUT_SIZE);
      printf("%s\n", tmp);
    }
    /* REDIRECT IN */
    else if(strstr(input, "<") != 0) {
      dir = 0;
      handle_redir(input, cmd1, file, dir);
    }
    /* REDIRECT OUT */
    else if(strstr(input, ">") != 0) {
      dir = 1;
      handle_redir(input, cmd1, file, dir);
    }
    /* PIPING */
    else if(strstr(input, "|") != 0) {
      handle_cmd2(input, cmd1, cmd2);
    }
    /* BACKGROUNDING */
    else if(strstr(input, "&") != 0) {
      bg = 1;
      handle_cmd1(input, cmd1, bg);
    }
    /* STANDARD EXECUTION */
    else {
      bg = 0;
      handle_cmd1(input, cmd1, bg);
    }
  }// end while
  return 0;
}

/***************************************/
/*      function change directory      */
/***************************************/
int cdcmd(char* input)
{
  char *it = input;                 /* it(erator) for input */
  int index = 0;                    /* index for array */
  char tmp[INPUT_SIZE];             /* buffer for general use */
  char *strarray[ARRAY_SIZE];       /* array of pointers for strings */
  
  bzero(tmp, INPUT_SIZE);           /* zero the buffer to toss out errors */

  while(*it == ' ') {it++;}         /* skip whitespace */
  while(*it != '\0') {
    /* grabs the last item before the ' ' */
    if((*it == ' ')) {
      strarray[index] = (char *) malloc(sizeof(char) * strlen(tmp) + 1); // space for '\0'
      strncpy(strarray[index], tmp, strlen(tmp));
      strncat(strarray[index], "\0", 1);
      bzero(tmp, INPUT_SIZE);
      index++;
    }
    /* else concatenate what the character is into the buffer until the delimiter is found */
     else {
       strncat(tmp, it, 1); 
     }
    it++;
  } // end while

  /* grab the last item before the '\0' */
  strarray[index] = (char *) malloc(sizeof(char) * strlen(tmp) + 1);
  strncpy(strarray[index], tmp, strlen(tmp));
  strncat(strarray[index], "\0", 1);
  bzero(tmp, INPUT_SIZE);
  index++;
  
  if(chdir(strarray[1]) != 0) {                   /* if the chdir fails, tell the user */
    fprintf(stderr, "Invalid directory \n");
    return 1;
  }
  /* set the current working directory after a successful change */
  setenv("PWD", getcwd(tmp, INPUT_SIZE), 1);
  return 0;

} // end cdcmd

/***************************************/
/*            function echo            */
/***************************************/
int echocmd(char * input)
{
  char * it = input;                // it(erator)
  char * env = (char *) 0;
  char tmp[INPUT_SIZE];  
  bzero(tmp, INPUT_SIZE);
   
  if(strstr(it, "$") != 0){
  while(*it == ' ') {it++;}
    while(*it != '$')
     it++;                           // advance over to the $ , assume this is env variable
    it++;
    while(*it != '\0') { 
      strncat(tmp, it, 1);
      it++;
    }
    strncat(tmp, "\0", 1);
    env = getenv(tmp);
    if(env == '\0') {
      fprintf(stderr, "$%s: Undefined Variable.\n", tmp);
      return 1;
    }
    else {
      printf("%s\n", env);
      return 0;
    }
  }
  else {
    while(*it != ' ')
      it++;
    while(*it == ' ')
      it++;
    printf("%s\n", it);
    // removed \n removal
  }
  return 0;
}

/***************************************/
/*        function handle_cmd1         */
/***************************************/
int handle_cmd1(char* input, char** cmd1, int bg)
{

  char* it = input; //it(erator)
  int index = 0;
  char* t;
  char tmp[INPUT_SIZE];
  bzero(tmp, INPUT_SIZE);
  
  while(*it != '\0') {
    if(*it == ' ') { // if index is zero, then must be leading space
      cmd1[index] = (char*) malloc(sizeof(char) * strlen(tmp) + 1);
      strncpy(cmd1[index], tmp, strlen(tmp));
      strncat(cmd1[index], "\0", 1);
      bzero(tmp, INPUT_SIZE); 
      index++;
    }
    else{
      strncat(tmp, it, 1);
    }
    it++;
  }

  int i, k;
  cmd1[index] = (char *) malloc(sizeof(char) * (strlen(tmp) + 1));
  strncpy(cmd1[index], tmp, strlen(tmp));
  strncat(cmd1[index], "\0", 1);
  bzero(tmp, INPUT_SIZE);
  index++;
  cmd1[index] = (char *) malloc(sizeof(char) * 1);
  cmd1[index] = (char*) 0;

  if(bg ==1) {
    for(i = 0;i < index; i++){
      if(strcmp(cmd1[i], "&") == 0){
	t = cmd1[i];
	for(k = i; k < index; k++)
	  cmd1[k] = cmd1[k + 1];
	//      	free(t);
	break;
      }
    }
  }
  /*    
  // this is for testing the cmds
  for(i = 0; i < index; ++i) {
    fprintf(stderr,"%s\n", cmd1[i]);
  }
  */

  if(bg == 1) 
   myexec(cmd1,1);
  else
   myexec(cmd1,0);

  /*
  for(k = 0; k < index; k++)
    free(cmd1[k]);
  */

  for(k = 0; k < 30; ++k){
    cmd1[k] = (char*) malloc(sizeof(char) * INPUT_SIZE);
    cmd1[k] = (char*) 0;
  }
 
// free();
// what about removing & character
}

/***************************************/
/*        function handle_cmd2         */
/***************************************/
int handle_cmd2(char* input, char** cmd1, char** cmd2)
{
  // for before and after the pipe, then call his mypipe
  char* it = input; //it(erator)
  int index = 0, index2 = 0;
  char tmp[INPUT_SIZE];
  bzero(tmp, INPUT_SIZE);
  
  while(*it != '|') {
    if(*it == ' ') { // if index is zero, then must be leading space
      cmd1[index] = (char*) malloc(sizeof(char) * strlen(tmp) + 1);
      strncpy(cmd1[index], tmp, strlen(tmp));
      strncat(cmd1[index], "\0", 1);
      bzero(tmp, INPUT_SIZE); 
      index++;
    }
    else
      strncat(tmp, it, 1);
    it++;
  }
  if(*(--it) != ' ') {
    it++;
    cmd1[index] = (char*) malloc(sizeof(char) * strlen(tmp) + 1);
    strncpy(cmd1[index], tmp, strlen(tmp));
    strncat(cmd1[index], "\0", 1);
    bzero(tmp, INPUT_SIZE);
    index++;
  } // if
  
  cmd1[index] = (char *) malloc(sizeof(char) * 1);
  cmd1[index] = (char*) 0;

  while(!isalpha(*it)) {it++;}
  while(*it != '\0') {
    if((*it == ' ')) { // if index is zero, then must be leading space
      cmd2[index2] = (char*) malloc(sizeof(char) * strlen(tmp) + 1);
      strncpy(cmd2[index2], tmp, strlen(tmp));
      strncat(cmd2[index2], "\0", 1);
      bzero(tmp, INPUT_SIZE); 
      index2++;
    }
    else
      strncat(tmp, it, 1);
    it++;
  }
  cmd2[index2] = (char*) malloc(sizeof(char) * (strlen(tmp) + 1));
  strncpy(cmd2[index2], tmp, (strlen(tmp)));
  strncat(cmd2[index2], "\0", 1);
  bzero(tmp, INPUT_SIZE);
  index2++;
  cmd2[index2] = (char *) malloc(sizeof(char) * 1);
  cmd2[index2] = (char*) 0;
  /*
  int i;
  for(i = 0; i < index; ++i)
    printf("%s\n", cmd1[i]);
  for(i = 0; i < index2; ++i)
    printf("%s\n", cmd2[i]);
  */
  mypipe(cmd1, cmd2);
}

/***************************************/
/*       function handle_redir         */
/***************************************/
int handle_redir(char* input, char** cmd1,char* file,  int dir)
{
  char* it = input; //it(erator)
  int index = 0;
  char tmp[INPUT_SIZE];
  bzero(tmp, INPUT_SIZE);
  if(dir == 1) {
    while(*it != '>') {
      if(*it == ' ') {
	cmd1[index] = (char*) malloc(sizeof(char) * strlen(tmp) + 1);
	strncpy(cmd1[index], tmp, strlen(tmp));
	strncat(cmd1[index], "\0", 1);
	bzero(tmp, INPUT_SIZE); 
	index++;
      }
      else
	strncat(tmp, it, 1);
    /*
    if(strncmp(cmd1[index], " ", 1)== 0) {
      index--;  
    }
    */
    it++;
    } // end while
  } //end if
  else {
    while(*it != '<') {
      if(*it == ' ') {
	cmd1[index] = (char*) malloc(sizeof(char) * strlen(tmp) + 1);
	strncpy(cmd1[index], tmp, strlen(tmp));
	strncat(cmd1[index], "\0", 1);
	bzero(tmp, INPUT_SIZE); 
	index++;
      }
      else
	strncat(tmp, it, 1);
    /*
    if(strncmp(cmd1[index], " ", 1)) {
      index--;  
    }
    */
    it++;
    } // end while
  } // end else
  if(*(--it) != ' ') {
    it++;
    cmd1[index] = (char*) malloc(sizeof(char) * strlen(tmp) + 1);
    strncpy(cmd1[index], tmp, strlen(tmp));
    strncat(cmd1[index], "\0", 1);
    bzero(tmp, INPUT_SIZE);
    index++;
  } // if
  cmd1[index] = (char *) malloc(sizeof(char) * 1);
  cmd1[index] = (char *)0;
  //end of getting command

  // pass up leading whitespace and the redirect char for input
  while(!isalpha(*it)) {it++;}
  while(*it != '\0') { // assume the rest is a filename
    strncat(file, it, 1);
    it++;
  }
  strncat(file, "\0", 1);
  
  /* test code for cmd and file
  int i;
    for(i = 0; i < index; ++i)
      printf("%s \n", cmd1[i]);
    printf("%s\n", file);
  */

  if(dir == 1) 
    redirect_out(cmd1, file); 
  else
    redirect_in(cmd1, file);

// free();
}

int myexec(char** cmd, int bg)
{
  pid_t pid;
  int status, i;
  if(addpath(cmd) == 1) {

    pid = fork(); // check for proper forking

    if(pid == 0) {
      if(execv(cmd[0], cmd) < 0) {
	perror("execv");
	return 0;
      }
    }
    else {
      if(bg == 0) {
	waitpid(pid, &status, 0);
      }
      else {
	waitpid(pid, &status, WNOHANG);      
      }
    }
  }
  else {
    printf("Cannot execute %s : no such program\n", cmd[0]);
    return 0;
  }
  return 1;

} // end myexec

void death()
{
  int status;
   waitpid(0, &status, WNOHANG);
   signal(SIGCHLD, &death);
  return;
}

int addpath(char** cmd)
{
  int index= 0, i, k, fd;
  char *path = getenv("PATH");
  char *sp[ARRAY_SIZE]; // search path C-string array 
  char tmp[INPUT_SIZE]; // temp array to copy values into and out of
  char *tempz = NULL;
  bzero(tmp, 100);

  if(cmd[0][0] == '/') { NULL; }
  else if (cmd[0][0] == '.') { NULL; }
  else {  
  
    while(*path != '\0') {
      if(*path == '.') {
	path++;
	if((*path == ':') || (*path == ' ') || (*path == '\0')) {
	  getcwd(tmp, INPUT_SIZE);
	  sp[index] = (char *) malloc(sizeof(char) * (strlen(tmp) + 2));
	  strncat(sp[index], tmp, strlen(tmp));
	  strncat(sp[index], "/", 1);
	  strncat(sp[index], "\0", 1);
	  index++;
	  bzero(tmp, INPUT_SIZE);
	  path--;
	}
	else {
	  path--;
	  strncat(tmp, ".", 1);
	}
      }// end if '.'
      if(*path == ':') {
	strncat(tmp, "/", 1);
	sp[index] = (char *) malloc(sizeof(char) * strlen(tmp) + 1);
	strncat(sp[index], tmp, strlen(tmp));
	strncat(sp[index], "\0", 1);
	index++;
	bzero(tmp, INPUT_SIZE);
      }
      else {
	strncat(tmp, path, 1);
      }
      path++;

    } // end while
  }
    bzero(tmp, INPUT_SIZE);

    for(i = 0; i < index; i++) {
      strcpy(tmp, sp[i]);
      strncat(tmp, cmd[0], strlen(cmd[0]));

      if((fd = open(tmp, O_RDONLY)) > 0 ) {
	cmd[0] = (char *) malloc(sizeof(char) * (strlen(tmp)));
	strncpy(cmd[0], tmp, strlen(tmp));
	close(fd);

	for(k = 0; k < ARRAY_SIZE; ++k)
	  sp[k] = (char*) malloc(sizeof(char) * ARRAY_SIZE);
	return 1;
      } // end if
      bzero(tmp, INPUT_SIZE);
      fd = 0;
    } // for

    for(k = 0; k < ARRAY_SIZE; ++k)
      sp[k] = (char*) malloc(sizeof(char) * 1);
    return 0;

} // end addpath

int redirect_out(char** cmd, char* file)
{
  int fd = 0, status;
  pid_t pid;

  if(addpath(cmd) == 1){
    if(file[0] != '\0') {
      fd = open(file, O_CREAT|O_WRONLY, 00666);
    }
    else {
      return 0;
    }
    if (fd < 0) {
      printf("Cannot open %s: Permission denied.\n", file);
    }
    pid = fork();
    if(pid == 0){
      close(1);
      dup(fd);
      close(fd);
      execv(cmd[0], cmd);
      return 0;
    }
    else {
      waitpid(pid, &status, 0);
    }
  } // if addpath
  else {
    printf("Cannot execute %s : invalid path or filename\n", cmd[0]);
    return 0;
  }
  return 1;
} // redirect_out

int redirect_in(char** cmd, char* file)
{
  int fd, status;
  pid_t pid;

  if(addpath(cmd) == 1){
    if(file[0] != '\0') {
      fd = -1;
      fd = open(file, O_RDONLY);
    }
    else {
      return 0;
    }
    if(fd < 0) {
      printf("%s: No such file or directory.\n", file);
      return 0;
    }
    pid = fork();
    if(pid ==0) {
      close(0);
      dup(fd);
      close(fd);
      execv(cmd[0], cmd);
      return 0;
    }
    else {
      waitpid(pid, &status, 0);
    }
  }
  else {
    printf("Cannot execute %s: invalid path or filename", cmd[0]);
    return 0;
  }
  return 1;
} // end redirect_out

int mypipe(char** cmd1, char** cmd2) 
{
  pid_t pid1, pid2;
  int fds[2];
  int status1, status2;

  if((addpath(cmd1) == 1) && (addpath(cmd2) == 1)) {
    pipe(fds);
    pid1 = fork();
    if(pid1 == 0) {
      close(1);
      dup(fds[1]);
      close(fds[0]);
      close(fds[1]);
      execv(cmd1[0], cmd1);
      return 0;
    }

    pid2 = fork();
    if(pid2 == 0) {
      close(0);
      dup(fds[0]);
      close(fds[0]);
      close(fds[1]);
      execv(cmd2[0], cmd2);
      return 0;
    }

    close(fds[0]);
    close(fds[1]);
  }
  else {
    printf("One of the commands was not found in the path.\n");
    return 0;
  }
  return 1;
} // end mypipe

